import "ol/ol.css";
import Map from "ol/Map";
import View from "ol/View";
import XYZ from "ol/source/XYZ";
import * as olProj from "ol/proj";
import Overlay from "ol/Overlay";
import { Circle as CircleStyle, Fill, Stroke, Style } from "ol/style";
import { OSM, Vector as VectorSource } from "ol/source";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer";
import Feature from "ol/Feature";
import { Point, MultiLineString, LineString, Polygon } from "ol/geom";
import { fromLonLat } from "ol/proj";
import Circle from "ol/geom/Circle";
import { ScaleLine, defaults as defaultControls } from "ol/control";
import LinearRing from "ol/geom/LinearRing";
import { getTransform, transform } from "ol/proj";
import { unByKey } from "ol/Observable";
import WMTS from "ol/source/WMTS";
import WMTSTileGrid from "ol/tilegrid/WMTS";
import * as olExtent from "ol/extent";
import Icon from "ol/style/Icon";
import GeoJSON from "ol/format/GeoJSON";
import { getArea, getLength, getDistance } from "ol/sphere";
import Text from "ol/style/Text";
import TileWMS from "ol/source/TileWMS";
import { Draw, Modify, Snap } from "ol/interaction";
import * as olInteraction from "ol/interaction";
import gcj02Mecator from "./gcj02Mecator";
import { Heatmap as HeatmapLayer } from "ol/layer";
// 加载底图图层数据
import {
  OSMLayer,
  GoogleLayer,
  GDMapOnline,
  TMStreet,
  TMSatellite,
  TMTitle,
} from "./layers";

// 加载样式
import {
  PointDangerStyle,
  PointInfoStyle,
  PointPrimaryStyle,
  PointSuccessStyle,
  PointWarningStyle,
  SiteDangerStyle,
  SiteInfoStyle,
  SitePrimaryStyle,
  SiteSuccessStyle,
  SiteWarningStyle,
  PointStyle,
  CircleCustomStyle,
  DrawStyle,
  PolygonStyle,
  SectorGreenStyle,
} from "./style";

export default class MapObject {
  mouseLocation = [0, 0]; // 鼠标当前位置

  zoom = 13; // 当前地图层级

  range = [0, 0]; // 当前可视化区域

  polygonPointList = []; // 绘制完成多边形顶点列表

  drawModify = null;
  drawSnap = null;
  drawDraw = null;

  // 构造器函数
  constructor(
    domId = "map-ol",
    center = [116.403694, 39.924896],
    zoom = this.zoom,
    layerType = "OMS",
    config = {}
  ) {
    // 创建地图
    this.map = new Map({
      layers: this.getUnderlayLayerByName('TSLM'),
      target: domId,
      view: new View({
        center: fromLonLat(center),
        zoom,
        constrainResolution: true, // 设置缩放级别为整数
        smoothResolutionConstraint: false, // 关闭无级缩放地图
        minZoom: 1,
        maxZoom: 18,
      }),
      interactions: new olInteraction.defaults({
        doubleClickZoom: false, // 屏蔽双击放大事件
      }),
    });
    if (config.scale) {
      // 添加比例尺
      this.addScale();
    }
    if (config.mouseMove) {
      this.mouseMoveOnMap();
    }
    if (config.mapMoved) {
      this.mapMovedOnMap();
    }
    this.addWAPIAN()
  }


  // 加载本地瓦片图
  addWAPIAN() {
    let openStreetMapLayer = new TileLayer({
      source: new XYZ({
        projection: gcj02Mecator,
        url: 'http://127.0.0.1/img/{z}/{x}/{y}.png'
      })
    });
    this.map.addLayer(openStreetMapLayer);
  }


  // 添加比例尺
  addScale() {
    let control = new ScaleLine({});
    this.map.addControl(control);
  }

  // 鼠标移动事件
  mouseMoveOnMap() {
    this.map.on("pointermove", (ev) => {
      let mouse = ev.coordinate; // 获取 3857 坐标
      this.mouseLocation = transform(mouse, "EPSG:3857", "EPSG:4326"); // 坐标转换
    });
  }

  // 地图移动完成事件
  mapMovedOnMap() {
    this.map.on("moveend", (e) => {
      this.getSize();
    });
  }

  // 获取可视化区域
  getSize() {
    this.zoom = this.map.getView().getZoom();
    let range = this.map.getView().calculateExtent(this.map.getSize());
    let northEast = transform([range[2], range[3]], "EPSG:3857", "EPSG:4326");
    let southWest = transform([range[0], range[1]], "EPSG:3857", "EPSG:4326");
    this.range =
      northEast[0].toFixed(5) +
      "," +
      northEast[1].toFixed(5) +
      " - " +
      southWest[0].toFixed(5) +
      "," +
      southWest[1].toFixed(5);
  }

  // 切换图层
  changeMap(layerTypeName) {
    let underlayLayer = this.getLayerFromMap("underlayLayer");
    let underlayLayerForTMTitle = this.getLayerFromMap(
      "underlayLayerForTMTitle"
    );
    this.map.removeLayer(underlayLayer);
    this.map.removeLayer(underlayLayerForTMTitle);
    this.getUnderlayLayerByName(layerTypeName).forEach((layer) => {
      this.map.addLayer(layer);
    });
  }

  // 根据名称获取可使用底图对象
  getUnderlayLayerByName(layerTypeName) {
    let layers = [];
    switch (layerTypeName) {
      case "GDM":
        layers = [GDMapOnline];
        break;
      case "TSM":
        layers = [TMStreet, TMTitle];
        break;
      case "TSLM":
        layers = [TMSatellite, TMTitle];
        break;
      case "Google":
        layers = [GoogleLayer];
        break;
      default:
        layers = [OSMLayer];
        break;
    }
    return layers;
  }

  // 根据图层名称获取图层
  getLayerFromMap(name) {
    let layers = this.getAllLayers();
    let layer = null;
    layers.forEach((item) => {
      if (item.values_.title !== undefined) {
        if (item.values_.title === name) {
          layer = item;
          return layer;
        }
      }
    });
    return layer;
  }

  // 获取地图引擎加载完成的全部图层
  getAllLayers() {
    return this.map.getLayers();
  }

  /**
   * 根据图层名称， 添加新图层到地图引擎
   * @param {*} layerName 自定义图层名称
   * @param {*} clear 如果存在是否清空之前数据源
   * @param {*} zIndex 底图层级
   * @param {*} opacity 底图透明度
   */
  addVectorLayer(layerName, clear = true, zIndex = 1, opacity = 1) {
    let targetLayer = this.getLayerFromMap(layerName); // 根据图层名称查询是否存在图层
    if (!targetLayer) {
      // 如果目标图层不存在，则创建
      let source = new VectorSource();
      let layer = new VectorLayer({
        title: layerName,
        opacity,
        zIndex,
      });
      layer.setSource(source);
      this.map.addLayer(layer);
      return layer;
    } else {
      // 获取目标图层下的数据源
      let source = targetLayer.getSource();
      // 清除数据源中原有覆盖物
      if (clear) {
        source.clear();
      }
      return targetLayer;
    }
  }

  /**
   * 根据图层名称，在地图引擎移除目标图层
   * @param {*} layerName
   */
  removeVectorLayer(layerName) {
    let targetLayer = this.getLayerFromMap(layerName); // 根据图层名称查询是否存在图层
    if (targetLayer) {
      // 如果存在目标图层
      this.map.removeLayer(targetLayer); // 从地图引擎移除图层
    }
  }

  /**
   * 地图中心坐标位置切换
   * @param {*} center 中心点坐标列表 [经度，纬度]
   * @param {*} duration 移动时间（毫秒）
   */
  setCenter(center, duration = 1000) {
    this.map.getView().animate({
      // 只设置需要的属性即可
      center: fromLonLat(center), // 中心点
      duration: duration, // 缩放持续时间，默认不需要设置
    });
  }

  /**
   * 添加坐标点
   * @param {*} layerName 图层名称
   * @param {*} dataList 图层数据列表
   * @param {*} clear 是否清除之前数据
   * @param {*} zIndex 图层层级
   * @param {*} opacity 图层透明度
   */
  addPointIcons(
    layerName,
    dataList = [],
    clear = true,
    zIndex = 1,
    opacity = 1
  ) {
    this.addVectorLayer(layerName, clear, zIndex, opacity); // 创建或清空目标图层
    let features = []; // 特征集合
    dataList.forEach((item) => {
      let feature = new Feature({
        id: item.id,
        geometry: new Point(fromLonLat([item.longitude, item.latitude])),
      });
      switch (item.type) {
        case "warning":
          feature.setStyle(PointWarningStyle);
          break;
        case "success":
          feature.setStyle(PointSuccessStyle);
          break;
        case "info":
          feature.setStyle(PointInfoStyle);
          break;
        case "danger":
          feature.setStyle(PointDangerStyle);
          break;
        default:
          feature.setStyle(PointPrimaryStyle);
      }
      feature.setId(item.id);
      feature.set("data", item);
      features.push(feature);
    });
    // 获取目标图层
    let targetLayer = this.getLayerFromMap(layerName);
    // 获取目标图层下的数据
    let source = targetLayer.getSource();
    source.addFeatures(features);
  }

  /**
   * 添加坐标点
   * @param {*} layerName 图层名称
   * @param {*} dataList 图层数据列表
   * @param {*} clear 是否清除之前数据
   * @param {*} zIndex 图层层级
   * @param {*} opacity 图层透明度
   */
  addSiteIcons(
    layerName,
    dataList = [],
    clear = true,
    zIndex = 1,
    opacity = 1
  ) {
    this.addVectorLayer(layerName, clear, zIndex, opacity); // 创建或清空目标图层
    let features = []; // 特征集合
    dataList.forEach((item) => {
      let feature = new Feature({
        id: item.id,
        geometry: new Point(fromLonLat([item.longitude, item.latitude])),
      });
      switch (item.type) {
        case "warning":
          feature.setStyle(SiteWarningStyle);
          break;
        case "success":
          feature.setStyle(SiteSuccessStyle);
          break;
        case "info":
          feature.setStyle(SiteInfoStyle);
          break;
        case "danger":
          feature.setStyle(SiteDangerStyle);
          break;
        default:
          feature.setStyle(SitePrimaryStyle);
      }
      feature.setId(item.id);
      feature.set("data", item);
      features.push(feature);
    });
    // 获取目标图层
    let targetLayer = this.getLayerFromMap(layerName);
    // 获取目标图层下的数据
    let source = targetLayer.getSource();
    source.addFeatures(features);
  }

  /**
   * 添加点标识
   * @param {*} layerName 图层名称
   * @param {*} dataList 数据列表
   * @param {*} color 点坐标颜色
   * @param {*} radius 点坐标半径
   * @param {*} clear 是否清除之前数据
   * @param {*} zIndex 图层层级
   * @param {*} opacity 图层透明度
   */
  addPoint(
    layerName,
    dataList = [],
    color = "red",
    radius = 5,
    clear = true,
    zIndex = 1,
    opacity = 1
  ) {
    this.addVectorLayer(layerName, clear, zIndex, opacity); // 创建或清空目标图层
    let features = []; // 特征集合
    dataList.forEach((item) => {
      let feature = new Feature({
        id: item.id,
        geometry: new Point(fromLonLat([item.longitude, item.latitude])),
      });
      feature.setStyle(PointStyle(color, radius));
      feature.setId(item.id);
      feature.set("data", item);
      features.push(feature);
    });
    // 获取目标图层
    let targetLayer = this.getLayerFromMap(layerName);
    // 获取目标图层下的数据
    let source = targetLayer.getSource();
    source.addFeatures(features);
  }

  /**
   * 添加覆盖圆
   * @param {*} layerName 图层名称
   * @param {*} dataList 数据列表
   * @param {*} color 覆盖圆颜色
   * @param {*} radius 覆盖圆半径/米
   * @param {*} clear 是否清楚之前图层已有数据
   * @param {*} zIndex 图层层级
   * @param {*} opacity 图层透明度
   */
  addCircle(
    layerName,
    dataList = [],
    color = "red",
    radius = 500,
    clear = true,
    zIndex = 1,
    opacity = 1
  ) {
    this.addVectorLayer(layerName, clear, zIndex, opacity); // 创建或清空目标图层
    let features = []; // 特征集合
    dataList.forEach((item) => {
      let feature = new Feature({
        id: item.id,
        geometry: new Circle(
          fromLonLat([item.longitude, item.latitude]),
          radius
        ),
      });
      feature.setStyle(CircleCustomStyle(color));
      feature.setId(item.id);
      feature.set("data", item);
      features.push(feature);
    });
    // 获取目标图层
    let targetLayer = this.getLayerFromMap(layerName);
    // 获取目标图层下的数据
    let source = targetLayer.getSource();
    source.addFeatures(features);
  }

  /**
   * 鼠标绘制多边形(第一步)
   */
  drawPolygon(layerName = "drawPolygon") {
    return new Promise((resolve, reject) => {
      this.polygonPointList = [];
      this.removeInteractionForPolygon(); // 移除所有交互
      this.addVectorLayer(layerName); // 添加绘制图层
      let targetLayer = this.getLayerFromMap(layerName); // 获取目标图层
      targetLayer.setStyle(DrawStyle); // 设置样式
      let source = targetLayer.getSource(); // 获取目标图层下的数据
      this.drawModify = new Modify({
        source: source,
      });
      this.drawSnap = new Snap({
        source: source,
      });
      this.drawDraw = new Draw({
        source: source,
        type: "Polygon",
      });
      this.map.addInteraction(this.drawModify);
      this.map.addInteraction(this.drawSnap);
      this.map.addInteraction(this.drawDraw);
      this.drawDraw.on("drawend", (e) => {
        //绘画完成触发时间
        const geometry = e.feature.getGeometry();
        const corrdinates = geometry.getCoordinates();
        let points = [];
        corrdinates[0].forEach((item) => {
          let xy = transform(item, "EPSG:3857", "EPSG:4326"); // 转换成经纬度坐标
          points.push(xy);
        });
        this.polygonPointList = points;
        this.removeInteractionForPolygon();
        resolve(this.polygonPointList);
      });
    });
  }

  /**
   * 编辑绘制图形(第二步)
   */
  editDrawPolygon() {
    this.map.addInteraction(this.drawModify);
    this.map.addInteraction(this.drawSnap);
  }

  /**
   * 完成编辑(第三步)
   * @param {*} layerName 图层名称
   */
  finishDrawPolygon(layerName) {
    return new Promise((resolve, reject) => {
      let targetLayer = this.getLayerFromMap(layerName);
      let source = targetLayer.getSource();
      let feature = source.getFeatures()[0];
      const geometry = feature.getGeometry();
      const corrdinates = geometry.getCoordinates();
      let points = [];
      corrdinates[0].forEach((item) => {
        let xy = transform(item, "EPSG:3857", "EPSG:4326");
        points.push(xy);
      });
      this.polygonPointList = points;
      this.removeInteractionForPolygon();
      resolve(this.polygonPointList);
    });
  }

  /**
   * 移除绘制相关交互(第四步)
   */
  removeInteractionForPolygon() {
    this.map.removeInteraction(this.drawModify);
    this.map.removeInteraction(this.drawSnap);
    this.map.removeInteraction(this.drawDraw);
  }

  /**
   * 添加一个覆盖物
   * @param {*} layerName 图层名称
   * @param {*} polygonPointList 多边形顶点列表
   */
  addPolygon(layerName, polygonPointList) {
    this.addVectorLayer(layerName); // 添加绘制图层
    let targetLayer = this.getLayerFromMap(layerName); // 获取目标图层
    let source = targetLayer.getSource();
    let polygon = new Polygon([polygonPointList]);
    polygon.applyTransform(getTransform("EPSG:4326", "EPSG:3857"));
    let feature = new Feature(polygon);
    feature.setStyle(PolygonStyle);
    source.addFeature(feature);
  }

  /**
   * 添加扇形标识
   * @param layerName 图层名称
   * @param dataList 数据源
   * @param clear 是否清除之前图层数据
   */
  addSectorIcon(layerName = "sectorLayer", dataList = [], clear = true) {
    this.addVectorLayer(layerName, clear); // 创建或清空目标图层
    let features = []; // 特征集合
    dataList.forEach((item) => {
      let feature = new Feature({
        id: item.id,
        geometry: new Point(fromLonLat([item.longitude, item.latitude])),
      });
      feature.setStyle(SectorGreenStyle(item.type, item.angle));
      feature.setId(item.id);
      feature.set("data", item);
      features.push(feature);
      // 获取目标图层
      let targetLayer = this.getLayerFromMap(layerName);
      // 获取目标图层下的数据
      let source = targetLayer.getSource();
      source.addFeatures(features);
    });
  }
}
